接上篇
Perl 脚本中单元测试自动化浅析(一)https://www.ym97.com/wenku/perl/8924.shtml
然而,你可能在最初并不能预见到底需要测试多少个 testcase。为此 Test::More 提供了另外一种在最下方用 done_testing 的方式来达到这个目的。对应的代码如下:
use Test::More;
…… run your tests ……
done_testing($number_of_tests_run);
这时,你甚至可以用 skip_all 来跳过 testcase。
1
use Test::More skip_all => $skip_reason;
Test::More 中提供了许多使用的方法,表 1 中列举出了其中的一些。
表 1. 常用 Test::More 方法
|
方法 |
说明 |
用法 |
|
ok |
判断 testcase ok |
ok($got op $expected,$test_name); |
|
is/isnt |
字符串比较 |
is($got,$expected,$test_name); |
|
|
|
isnt($got,$expected,$test_name); |
|
like/unlike |
正则表达式比较 |
like( $got, qr/expected/, $test_name ); |
|
nlike( $got, qr/expected/, $test_name ); |
|
cmp_ok |
可以指定操作符地比较 |
cmp_ok($got,$op,$expected,$test_name); |
|
can_ok |
被测模块或对象的方法 |
can_ok($module,@methods) |
|
|
|
can_ok($object,@methods) |
|
isa_ok |
对象是否被定义或对象的实例变量确实是已定义的引用 |
isa_ok($object,$class,$object_name); |
|
isa_ok($subclass,$class,$object_name); |
|
isa_ok($ref,$type,$ref_name); |
|
subtest |
测试子集 |
subtest $name=>&code; |
|
pass/fail |
直接给出通过 / 不通过 |
pass($test_name); |
|
|
|
fail($test_name); |
|
use_ok |
测试加载模块并导入相应符号是否成功 |
BEGIN {use_ok($module);} |
|
BEGIN {use_ok($module,@imports);} |
|
is_deeply |
复杂数据结构的比较 |
is_deeply($got,$expected,$test_name); |
|
new_ok |
判断创建的对象是否 ok |
my $obj=new_ok($class); |
|
my $obj=new_ok($class=>@args); |
|
my $obj=new_ok($class=>@args,$object_name); |
这里,我们着重介绍其中的几个。
is(Arg1, Arg2, Arg3)
类似于 ok(), 用 eq 操作符比较 Arg1 和 Arg2 的值来决定 testcase 成功还是失败。Arg3 是指 testcase 的名字。
like( Arg1, Arg2, Arg3 )
Arg2 是一个正则表达式 , 比较 Arg1 是否 匹配 Arg2 正则表达式。Arg3 是指 testcase 的名字。
cmp_ok( Arg1, Arg2, Arg3, Arg4 );
cmp_ok() 允许用任何二元操作符(Arg2)比较 Arg1,Arg3. 同样,Arg4 是指 testcase 的名字。另外 cmp_ok() 有个好处,如果 testcase failed, 结果中会报告 Arg1 和 Arg2 在运行中的实际值。
can_ok($module, @methods); can_ok($object, @methods);
can_ok() 判断模块 $module 或对象 $object 能否调用方法 @methods。
更多方法可以参考 CPAN 上关于 Test::More 的更多的介绍。
基于这些函数,我们能非常方便的设计和实现基于 Test::More 的 testcase。示例代码 test_more.perl 如下:
清单 4. Test::More 示例代码
use strict;
use warnings;
use Test::More tests => 3;
use Hello; # What you're testing.
my $hellostr=Hello::hello('guys');
my $byestr=Hello::bye('guys');
is($hellostr, 'Hello, guys!', 'hello() works');
like($byestr, "/Goodbye/", 'bye() works');
cmp_ok($hellostr, 'eq', 'Hello, guys!', 'bye() works');
can_ok('Hello', qw(hello bye));
在命令行下运行 perl test_more.perl 后,我们可以看到程序的输出如下:
清单 5. Test::More 示例代码执行结果
C:Perl est> perl more.perl
1..3
ok 1 - hello() works
ok 2 - bye() works
ok 3 - bye() works
ok 4 - Hello->can(...)
# Looks like you planned 3 tests but ran 4.
cmp_ok($hellostr, 'eq', 'Hello, guys!', 'bye() works');
can_ok('Hello', qw(hello bye));
Test::Class 的介绍
定义一个测试类,只需要编写一个从 Test::Class 继承的子类,申明如下:
1
use base qw(Test::Class);
由于 Test::Class 本身没有提供测试函数,而是使用 Test::More 之类的其他测试框架的方法,因此需要申明 Test::More 模块 :
1
use Test::More;
Test::Class 的常用方法包括:
Test 方法
sub method_name:Test {...};
sub method_name:Test(N){...};
列表项中可以包含代码清单,表格和图片(例如一系列图片以列表项的形式组织到一起);
N: 代表函数内测试判断执行数量,相当于执行 case 数目。默认代表只执行 1 个 case. 如果你无法判断执行 case 数目,如循环执行 case。那么,可以使用 sub method_name:Test(no_plan) {...} 或 sub method_name:Tests{...}。
Setup 和 teardown 方法
setup 和 teardown 分别在每个普通测试方法之前和之后调用。
Setup 用法
sub method_name:Test(setup) {...};
sub method_name:Test(setup=>N){...};
teardown 用法
sub method_name : Test(teardown) { ... };
sub method_name : Test(teardown => N) { ... };
startup 和 shutdown 方法
startup 和 shutdown 方法用法和 Setup,teardown 方法类似,区别在与 Startup 和 shutdown 是在所有测试方法执行之前和之后调用。
Runttests 方法
通过调用 runtests() 方法,执行所有从 Test::Class 派生的子类中定义的测试 case,用法:
Test::Class->runtests();
下面的例子是基于 Test::Class 的测试代码 test_class.perl。
清单 6. Test::Class 示例代码
use strict;
use warnings;
use Hello; # What you're testing.
use Test::More;
use base qw(Test::Class);
my $hellostr=Hello::hello('guys');
my $byestr=Hello::bye('guys');
sub initial : Test(setup) {
print "Begin One Test... ";
}
sub end : Test(teardown) {
print "End One Test... ";
}
sub test_hello : Test(1) {
is($hellostr, 'Hello, guys!', 'hello() works');
}
sub test_bye : Test(1) {
like($byestr, "/Goodbye/", 'bye() works');
}
Test::Class->runtests();
在命令行中执行 perl test_class.perl 后的测试结果如下:
清单 7. Test::Class 示例代码执行结果
C:Perl est> perl test_class.perl
Begin One Test...
1..2
ok 1 - bye() works
End One Test...
Begin One Test...
ok 2 - hello() works
End One Test...
应用实例
有了之前对于几个常用 Perl 单元测试框架的介绍,下面我们给出一个具体的实例,来测试一个 CPAN 中的一个 module File::Util。部分单元测试的代码如下。
清单 8. 应用实例代码
#!/usr/bin/perl -w
use strict;
use File::Util;
use Test::More;
use base qw(Test::Class);
my $file;
sub init : Test(startup){
print "#################################################### ";
print "This script is used to test some subs in File::Util. ";
$file = File::Util->new();
}
sub shutdown : Test(shutdown){
print "Finished all testcases. ";
print "#################################################### ";
}
sub initial : Test(setup) {
print "---------------------------------------------------- ";
print "Begin One Test... ";
}
sub end : Test(teardown) {
print "End One Test... ";
print "---------------------------------------------------- ";
}
sub test_methods : Test(1) {
can_ok('File::Util', qw(existent line_count list_dir));
}
sub test_existent_true : Test(1) {
open(FILE, ">test.txt");
print FILE "This is a test.";
close FILE;
cmp_ok($file -> existent('test.txt'), "==" ,1, 'test_existent_file_exists');
unlink "test.txt";
is($file -> existent('test.txt'), undef, 'test_existent_file_not_exists');
}
sub test_line_count : Test(1) {
open(FILE, ">test.txt");
print FILE "This is a test. ";
close FILE;
cmp_ok($file -> line_count('test.txt'), "==" ,1, 'test_line_count = 1');
open(FILE, ">test.txt");
print FILE "";
close FILE;
cmp_ok($file -> line_count('test.txt'), "==" ,0, 'test_line_count = 0');
}
Test::Class->runtests();
程序中设计了三个 testcase,分别用于测试模块中的方法的命名空间可见性、existent 方法和 line_count 方法。测试的结果如下:
清单 9. 应用实例代码执行结果
####################################################
This script is used to test some subs in File::Util.
----------------------------------------------------
Begin One Test...
1..3
ok 1 - test_existent_file_exists
ok 2 - test_existent_file_not_exists
# expected 1 test(s) in main::test_existent_true, 2 completed
End One Test...
----------------------------------------------------
----------------------------------------------------
Begin One Test...
ok 3 - test_line_count = 1
ok 4 - test_line_count = 0
# expected 1 test(s) in main::test_line_count, 2 completed
End One Test...
----------------------------------------------------
----------------------------------------------------
Begin One Test...
ok 5 - File::Util->can(...)
End One Test...
----------------------------------------------------
Finished all testcases.
####################################################
# Looks like you planned 3 tests but ran 5.
结束语
随着敏捷开发模式的流行,单元测试的自动化也显得尤其重要。本文介绍了 CPAN 上单元测试相关的几个模块 Test::Simple,Test::more 和 Test::class,并且结合实例具体讲解实现方法。一旦灵活掌握了这些模块使用方法将有助于提高软件开发和测试的效率。同时,CPAN 中还有许多使用的模块,灵活应用这些模块很很好的帮助我们高效和高质量的进行软件开发。